//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;

namespace Microsoft.TridentWordAddIn.Presentation
{
    /// <summary>
    /// Class ParametersUserControl
    /// </summary>
    public partial class ParametersUserControl : UserControl
    {
        private bool isInput = false;
        private List<WorkflowParameter> workflowParameters;

        /// <summary>
        /// Initializes a new instance of the <see cref="ParametersUserControl"/> class.
        /// </summary>
        public ParametersUserControl()
        {
            InitializeComponent();
            this.workflowParameters = new List<WorkflowParameter>();
        }

        #region Properties

        /// <summary>
        /// Gets or sets the header text.
        /// </summary>
        /// <value>The header text.</value>
        public string HeaderText
        {
            get
            {
                return this.lvFirst.Columns[0].Text;
            }
            set
            {
                this.lvFirst.Columns[0].Text = value;
            }
        }

        /// <summary>
        /// Gets the result.
        /// </summary>
        /// <value>The result.</value>
        public List<WorkflowParameter> Result
        {
            get
            {
                List<WorkflowParameter> result = new List<WorkflowParameter>();
                foreach (WorkflowParameter param in this.workflowParameters)
                {
                    if (param.IsInput == this.isInput)
                    {
                        result.Add(param);
                    }
                }

                return result;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Clears the pamrameters.
        /// </summary>
        public void ClearParameters()
        {
            this.tblpnlControls.Controls.Clear();
            this.AddColumnHeaders();
        }

        /// <summary>
        /// Validates the parameters.
        /// </summary>
        /// <returns></returns>
        public bool ValidateParameters()
        {
            bool isSuccess = true;
            this.errorProvider.Clear();
            this.errorProvider.BlinkStyle = ErrorBlinkStyle.NeverBlink;
            int index = 1;
            foreach (WorkflowParameter param in this.workflowParameters)
            {
                if (this.isInput == param.IsInput)
                {
                    if (!param.IsValid || param.DisplayValue.IsNullOrEmpty())
                    {
                        isSuccess = false;
                        Control ctrLabel = this.tblpnlControls.GetControlFromPosition(1, index);
                        ShowError(ctrLabel, param.Type);
                    }

                    index++;
                }
            }

            return isSuccess;
        }

        /// <summary>
        /// Updates the parameters.
        /// </summary>
        /// <param name="workflow">The workflow.</param>
        /// <param name="job">The job.</param>
        /// <param name="displayInputParameters">if set to <c>true</c> [display input parameters].</param>
        public void UpdateParameters(Workflow workflow, WorkflowJob job, bool displayInputParameters)
        {
            this.workflowParameters = CreateClone(workflow.Parameters);
            this.isInput = displayInputParameters;
            this.tblpnlControls.SuspendLayout();
            this.ClearParameters();
            int index = 1;
            string tooltipText = string.Empty;

            foreach (WorkflowParameter param in this.workflowParameters)
            {
                if (param.IsInput == this.isInput)
                {
                    JobParameter jobParameter = job.GetParameter(param.BelongsToActivity, param.Name);
                    Control lblControl = GetLabelControl(param.BelongsToActivity + "." + param.Name);
                    toolTip.SetToolTip(lblControl, GetToolTipText(param));
                    
                    string type = this.isInput ? param.Type : param.OutputType;
                    string value = jobParameter == null ? param.DisplayValue : jobParameter.Value;
                    Control valueControl = SelectTemplate(type, value, param);
                    this.ConfigureControlLayout(param, valueControl, value);

                    tblpnlControls.Controls.Add(lblControl, 0, index);
                    tblpnlControls.Controls.Add(valueControl, 1, index);
                    index++;
                }
            }

            this.tblpnlControls.ResumeLayout();
        }

        /// <summary>
        /// Updates the parameters.
        /// </summary>
        /// <param name="workflow">The workflow.</param>
        /// <param name="displayInputParameters">if set to <c>true</c> [display input parameters].</param>
        public void UpdateParameters(Workflow workflow, bool displayInputParameters)
        {
            this.workflowParameters = CreateClone(workflow.Parameters);
            this.isInput = displayInputParameters;
            this.tblpnlControls.SuspendLayout();
            this.ClearParameters();
            int index = 1;
            string tooltipText = string.Empty;

            foreach (WorkflowParameter param in this.workflowParameters)
            {
                if (param.IsInput == this.isInput)
                {                    
                    Control lblControl = GetLabelControl(param.BelongsToActivity + "." + param.Name);
                    toolTip.SetToolTip(lblControl, GetToolTipText(param));

                    string type = this.isInput ? param.Type : param.OutputType;
                    string value = param.DisplayValue;
                    Control valueControl = SelectTemplate(type, value, param);
                    this.ConfigureControlLayout(param, valueControl, value);

                    tblpnlControls.Controls.Add(lblControl, 0, index);
                    tblpnlControls.Controls.Add(valueControl, 1, index);
                    index++;
                }
            }

            this.tblpnlControls.ResumeLayout();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Determines whether [is null or empty] [the specified value].
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>
        /// 	<c>true</c> if [is null or empty] [the specified value]; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsNullOrEmpty(object value)
        {
            if (value == null)
            {
                return true;
            }

            if (value.ToString().Length == 0)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Gets the label control.
        /// </summary>
        /// <param name="label">The label.</param>
        /// <returns></returns>
        private static Control GetLabelControl(string label)
        {
            CustomLabel nameControl = new CustomLabel();
            nameControl.Left = 4;
            nameControl.Margin = new Padding(0, 8, 4, 0);
            nameControl.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
            nameControl.Text = label;
            nameControl.AutoEllipsis = true;
            nameControl.IsRequiredField = false;
            return nameControl;
        }

        /// <summary>
        /// Creates the clone.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        private static List<WorkflowParameter> CreateClone(List<WorkflowParameter> parameters)
        {
            List<WorkflowParameter> result = new List<WorkflowParameter>();
            foreach (WorkflowParameter param in parameters)
            {
                result.Add(param.Clone() as WorkflowParameter);
            }

            return result;
        }

        /// <summary>
        /// Adds the column headers.
        /// </summary>
        private void AddColumnHeaders()
        {
            tblpnlControls.Controls.Add(lvFirst, 0, 0);
            tblpnlControls.Controls.Add(lvSecond, 1, 0);
            lvFirst.Columns[0].Width = lvFirst.Width;
            lvSecond.Columns[0].Width = lvSecond.Width;
        }

        /// <summary>
        /// Removes the error icon.
        /// </summary>
        /// <param name="control">The control.</param>
        private void RemoveErrorIcon(Control control)
        {
            this.errorProvider.SetError(control, string.Empty);
        }

        /// <summary>
        /// Shows the error.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="paramType">Type of the param.</param>
        private void ShowError(Control control, string paramType)
        {
            string errorMessage = string.Format("Contains invalid data. Only data of type {0} is allowed for this parameter", paramType);
            this.errorProvider.SetError(control, errorMessage);
            this.errorProvider.SetIconAlignment(control, ErrorIconAlignment.TopLeft);
            this.errorProvider.SetIconPadding(control, -8);
        }

        /// <summary>
        /// Gets the tool tip text.
        /// </summary>
        /// <param name="param">The param.</param>
        /// <returns></returns>
        private string GetToolTipText(WorkflowParameter param)
        {
            StringBuilder toolTipText = new StringBuilder("Activity: ");
            toolTipText.Append(param.BelongsToActivity);
            toolTipText.AppendLine();

            toolTipText.Append("Property: ");
            toolTipText.Append(param.Name);
            toolTipText.AppendLine();

            toolTipText.Append("DataType: ");
            toolTipText.Append(param.Type);
            toolTipText.AppendLine();

            toolTipText.Append("Desc: ");
            toolTipText.Append(param.Description);
            toolTipText.AppendLine();

            if (!this.isInput)
            {
                toolTipText.Append("Output Label: ");
                toolTipText.Append(param.Label);
                toolTipText.AppendLine();

                toolTipText.Append("Output Type: ");
                toolTipText.Append(param.OutputType);
                toolTipText.AppendLine();
            }

            return toolTipText.ToString();
        }

        /// <summary>
        /// Selects the template.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="jobValue">The job value.</param>
        /// <param name="param">The param.</param>
        /// <returns></returns>
        private Control SelectTemplate(string type, string jobValue, WorkflowParameter param)
        {
            Control valueControl = null;
            if (!param.IsBound)
            {
                if (param.IsComplexType)
                {
                    valueControl = GetTypeInitializer(jobValue, param.Type, true);
                }
                else
                {
                    switch (type)
                    {
                        case "text/plain":
                        case "text/html":
                            valueControl = this.GetComboBox(jobValue, true);
                            break;
                        case "image/gif":
                        case "image/jpg":
                        case "image/bmp":
                        case "image/png":
                            valueControl = this.GetComboBox(jobValue, false);
                            break;
                        case "System.IO.FileInfo":
                        case "System.IO.DirectoryInfo":
                            valueControl = GetTypeInitializer(jobValue, param.Type, false);
                            break;
                        case "System.Boolean":
                            valueControl = this.GetBooleanComboBox(jobValue);
                            break;
                        default:
                            valueControl = this.GetTextBox(jobValue, false);
                            break;
                    }
                }
            }
            else
            {
                valueControl = this.GetTextBox(jobValue, true);
            }

            return valueControl;
        }

        /// <summary>
        /// Gets the combo box.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="isText">if set to <c>true</c> [is text].</param>
        /// <returns></returns>
        private Control GetComboBox(object value, bool isText)
        {
            ComboBox cmb = new ComboBox();
            cmb.SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
            cmb.DropDownStyle = ComboBoxStyle.DropDownList;
            string stringValue = value.ToString();
            if (isText)
            {
                cmb.Items.AddRange(new object[] { "text/plain", "text/html" });
                cmb.SelectedIndex = stringValue == "text/plain" ? 0 : 1;
            }
            else
            {
                int selectedIndex = 0;
                cmb.Items.AddRange(GetImageTypes(stringValue, ref selectedIndex));
                cmb.SelectedIndex = selectedIndex;
            }

            return cmb;
        }

        /// <summary>
        /// Gets the combo box.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private Control GetBooleanComboBox(object value)
        {
            ComboBox cmb = new ComboBox();
            cmb.SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);
            cmb.DropDownStyle = ComboBoxStyle.DropDownList;
            cmb.Items.Add(true);
            cmb.Items.Add(false);

            cmb.SelectedIndex = Convert.ToBoolean(value) ? 0 : 1;
            return cmb;
        }

        /// <summary>
        /// Gets the text box.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="isReadonly">if set to <c>true</c> [is readonly].</param>
        /// <returns></returns>
        private Control GetTextBox(object value, bool isReadonly)
        {
            TextBox textBox = new TextBox();
            textBox.TextChanged += new EventHandler(txt_TextChanged);
            textBox.ReadOnly = isReadonly;
            if (value != null)
            {
                textBox.Text = value.ToString();
            }

            return textBox;
        }

        /// <summary>
        /// Gets the type of the image.
        /// </summary>
        /// <param name="selectedType">Type of the selected.</param>
        /// <param name="selectedIndex">Index of the selected.</param>
        /// <returns></returns>
        private static object[] GetImageTypes(string selectedType, ref int selectedIndex)
        {
            string[] result = new string[] { "image/gif", "image/jpg", "image/bmp", "image/png" };
            foreach (string value in result)
            {
                if (value.CompareIgnoreCase(selectedType))
                {
                    break;
                }

                selectedIndex++;
            }

            return result;
        }

        /// <summary>
        /// Gets the type initializer.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="type">The type.</param>
        /// <param name="customType">if set to <c>true</c> [custom type].</param>
        /// <returns></returns>
        private Control GetTypeInitializer(object value, string type, bool customType)
        {
            try
            {
                TypeInitializerUserControl control = new TypeInitializerUserControl(value, type, customType);
                control.OnValueChanged += new EventHandler<EventArgs>(OnValueChanged);
                return control;
            }
            catch (Exception)
            {
                return this.GetTextBox(value, false);
            }
        }

        /// <summary>
        /// Gets the name of the parameter by.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="activity">The activity.</param>
        /// <returns></returns>
        private WorkflowParameter GetParameterByName(string name, string activity)
        {
            foreach (WorkflowParameter param in this.workflowParameters)
            {
                if (name == param.Name && activity == param.BelongsToActivity)
                {
                    return param;
                }
            }

            return null;
        }

        /// <summary>
        /// Configures the control layout.
        /// </summary>
        /// <param name="param">The param.</param>
        /// <param name="control">The control.</param>
        /// <param name="tooltipText">The tooltip text.</param>
        private void ConfigureControlLayout(WorkflowParameter param, Control control, string tooltipText)
        {
            control.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
            this.toolTip.SetToolTip(control, tooltipText);
            control.Tag = param;
            control.Left = 4;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the TextChanged event of the textBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void txt_TextChanged(object sender, EventArgs e)
        {
            TextBox textBox = sender as TextBox;
            if (textBox == null)
            {
                return;
            }

            WorkflowParameter selectedParam = textBox.Tag as WorkflowParameter;
            if (selectedParam != null)
            {
                WorkflowParameter param = this.GetParameterByName(selectedParam.Name, selectedParam.BelongsToActivity);
                param.Value = textBox.Text;
                toolTip.SetToolTip(textBox, textBox.Text);
                param.IsValid = !textBox.Text.IsNullOrEmpty();

                if (param.IsValid)
                {
                    RemoveErrorIcon(textBox);
                }
                else
                {
                    ShowError(textBox, param.Type);
                }
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the cmb control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cmb_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox comboBox = sender as ComboBox;
            if (comboBox == null)
            {
                return;
            }

            WorkflowParameter selectedParam = comboBox.Tag as WorkflowParameter;
            if (selectedParam != null)
            {
                WorkflowParameter param = this.GetParameterByName(selectedParam.Name, selectedParam.BelongsToActivity);
                if (comboBox.SelectedItem != null)
                {
                    param.Value = comboBox.SelectedItem;
                }
                else
                {
                    param.Value = param.Value;
                }

                toolTip.SetToolTip(comboBox, param.DisplayValue);
            }
        }

        /// <summary>
        /// Control_s the on value changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        private void OnValueChanged(object sender, EventArgs e)
        {
            TypeInitializerUserControl ctrTypeInitializer = sender as TypeInitializerUserControl;
            if (ctrTypeInitializer == null)
            {
                return;
            }

            WorkflowParameter parameter = ctrTypeInitializer.Tag as WorkflowParameter;
            if (parameter != null)
            {
                WorkflowParameter param = this.GetParameterByName(parameter.Name, parameter.BelongsToActivity);
                param.Value = ctrTypeInitializer.Value;
                param.IsValid = ctrTypeInitializer.IsValid;

                if (param.IsValid)
                {
                    RemoveErrorIcon(ctrTypeInitializer);
                }
                else
                {
                    ShowError(ctrTypeInitializer, param.Type);
                }
            }
        }

        /// <summary>
        /// Handles the Resize event of the lvFirst control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lvFirst_Resize(object sender, EventArgs e)
        {
            lvFirst.Columns[0].Width = lvFirst.Width;
        }

        /// <summary>
        /// Handles the Resize event of the lvSecond control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lvSecond_Resize(object sender, EventArgs e)
        {
            lvSecond.Columns[0].Width = lvSecond.Width;
        }

        #endregion

        private void lvFirst_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
        {
            tblpnlControls.ColumnStyles[0].Width = lvFirst.Columns[0].Width;
            tblpnlControls.Width = lvFirst.Width + lvSecond.Width;
        }

        private void lvSecond_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
        {
            tblpnlControls.ColumnStyles[1].Width = lvSecond.Columns[0].Width;
            tblpnlControls.Width = lvFirst.Width + lvSecond.Width;
        }
    }
}
